/**
 * Copyright (c) 2021 Wei-Lun Hsu. All Rights Reserved.
 */
/** @file zb32l03x_hal_dma.c
 *
 * @author Wei-Lun Hsu
 * @version 0.1
 * @date 2021/09/27
 * @license
 * @description
 */



#include "zb32l03x_hal.h"
#include <string.h>

/** @addtogroup ZB32L03x_HAL_Driver
  * @{
  */


/** @defgroup DMA DMA
  * @brief DMA HAL module driver.
  * @{
  */
#if defined(HAL_DMA_MODULE_ENABLED)
//=============================================================================
//                  Constant Definition
//=============================================================================
#define DMA_MODULE_NUM          1

/**
 *  cycle_ctrl field of DMA data control of a DMA descriptor. See PL230 documentation
 */
#define DMA_CYCLE_CTRL_INVALID                      0x0UL   /**< Invalid cycle type */
#define DMA_CYCLE_CTRL_BASIC                        0x1UL   /**< Basic cycle type */
#define DMA_CYCLE_CTRL_AUTO                         0x2UL   /**< Auto cycle type */
#define DMA_CYCLE_CTRL_PINGPONG                     0x3UL   /**< PingPong cycle type */
#define DMA_CYCLE_CTRL_MEM_SCATTER_GATHER           0x4UL   /**< Memory scatter gather cycle type */
#define DMA_CYCLE_CTRL_MEM_SCATTER_GATHER_ALT       0x5UL   /**< Memory scatter gather using alternate structure  */
#define DMA_CYCLE_CTRL_PERI_SCATTER_GATHER          0x6UL   /**< Peripheral scatter gather cycle type */
#define DMA_CYCLE_CTRL_PERI_SCATTER_GATHER_ALT      0x7UL   /**< Peripheral scatter gather cycle type using alternate structure */
#define DMA_CYCLE_CTRL_MASK                         0x7UL   /**< DMA Cycle control bit mask - basic/auto/ping-poing/scath-gath */


typedef enum dma_cycle_ctrl
{
    dma_cycle_ctrl_invalid                 = DMA_CYCLE_CTRL_INVALID,                    /**< Invalid cycle type */
    dma_cycle_ctrl_basic                   = DMA_CYCLE_CTRL_BASIC,                      /**< Basic cycle type */
    dma_cycle_ctrl_auto                    = DMA_CYCLE_CTRL_AUTO,                       /**< Auto cycle type */
    dma_cycle_ctrl_pingpong                = DMA_CYCLE_CTRL_PINGPONG,                   /**< Ping-Pong cycle type */
    dma_cycle_ctrl_mem_scatter_gather      = DMA_CYCLE_CTRL_MEM_SCATTER_GATHER,         /**< Memory scatter gather cycle type */
    dma_cycle_ctrl_mem_scatter_gather_alt  = DMA_CYCLE_CTRL_MEM_SCATTER_GATHER_ALT,     /**< Memory scatter gather using alternate structure  */
    dma_cycle_ctrl_peri_scatter_gather     = DMA_CYCLE_CTRL_PERI_SCATTER_GATHER,        /**< Peripheral scatter gather cycle type */
    dma_cycle_ctrl_peri_scatter_gather_alt = DMA_CYCLE_CTRL_PERI_SCATTER_GATHER_ALT,    /**< Peripheral scatter gather cycle type using alternate structure */

} dma_cycle_ctrl_t;
//=============================================================================
//                  Macro Definition
//=============================================================================
#define IS_WORD_ALIGN(x)             !((x) & 0x3)
#define IS_HALF_WORD_ALIGN(x)        !((x) & 0x1)

/**
 *  \brief  Check if DMA channel is enabled or not
 *
 *  \param [in] __HANDLE__      The pointer of a HAL_DMA_Handle_TypeDef structure
 *  \param [in] __CHANNEL__     DMA channel, reference enum HAL_DMA_Channel
 */
#define __HAL_DMA_IS_CHANNEL_ENABLE(__HANDLE__, __CHANNEL__)    \
            (((__HANDLE__)->Instance->CHENSET & (__CHANNEL__)))

/**
 *  \brief  Enable DMA channel
 *
 *  \param [in] __HANDLE__      The pointer of a HAL_DMA_Handle_TypeDef structure
 *  \param [in] __CHANNEL__     DMA channel, reference enum HAL_DMA_Channel
 */
#define __HAL_DMA_ENABLE_CHANNEL(__HANDLE__, __CHANNEL__)   \
            ((__HANDLE__)->Instance->CHENSET |= (__CHANNEL__))

/**
 *  \brief  Disable DMA channel
 *
 *  \param [in] __HANDLE__      The pointer of a HAL_DMA_Handle_TypeDef structure
 *  \param [in] __CHANNEL__     DMA channel, reference enum HAL_DMA_Channel
 */
#define __HAL_DMA_DISABLE_CHANNEL(__HANDLE__, __CHANNEL__)  \
            ((__HANDLE__)->Instance->CHENCLR |= (__CHANNEL__))

//=============================================================================
//                  Structure Definition
//=============================================================================
typedef struct DMA_Ctrl_Data
{
    union {
        volatile uint32_t    CTRL;
        struct {
            volatile uint32_t        cycle_ctrl     : 3;     /**< reference enum dma_cycle_ctrl */
            volatile uint32_t        next_useburst  : 1;
            volatile uint32_t        n_minus_1      : 10;    /**< 1 ~ 1024 */
            volatile uint32_t        r_power        : 4;     /**< arbitrate after each 2^r_power transfers. reference enum dma_r_power */
            volatile uint32_t        src_prot_ctrl  : 3;     /**< source protection, reference enum dma_prot */
            volatile uint32_t        dst_prot_ctrl  : 3;     /**< destination protection, reference enum dma_prot */
            volatile uint32_t        src_size       : 2;     /**< source data size, reference enum dma_data_size */
            volatile uint32_t        src_inc        : 2;     /**< source data increment, reference enum dma_data_inc */
            volatile uint32_t        dst_size       : 2;     /**< destination data size, reference dma_data_size */
            volatile uint32_t        dst_inc        : 2;     /**< destination data increment, reference enum dma_data_size */
        } CTRL_b;
    };
} DMA_Ctrl_Data_t;

typedef struct DMA_Descriptor_Pool
{
    DMA_Descriptor_t   Primary[DMA_CHANNEL_MAX];
    DMA_Descriptor_t   Alternate[DMA_CHANNEL_MAX];
} DMA_Descriptor_Pool_t;
//=============================================================================
//                  Global Data Definition
//=============================================================================
#ifdef  __GNUC__
static DMA_Descriptor_Pool_t       g_dma_descriptors[DMA_MODULE_NUM] __attribute__((aligned(1024)));
#else
__align(1024) static DMA_Descriptor_Pool_t       g_dma_descriptors[DMA_MODULE_NUM] = {0};
#endif

//=============================================================================
//                  Private Function Definition
//=============================================================================
/**
 *  @brief  Initializes the DMA MSP.
 *  @param [in] hDMA    pointer to a HAL_DMA_Handle_TypeDef structure that contains
 *                      the configuration information for DMA
 *  @return             None
 *  @details
 *      This function Should not be modified, when the callback is needed,
 *      the HAL_DMA_MspInit could be implemented in the user file
 */
__weak void HAL_DMA_MspInit(HAL_DMA_Handle_TypeDef *hDMA)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hDMA);
}


/**
 *  @brief  DeInitializes the DMA MSP.
 *  @param [in] hDMA    pointer to a HAL_DMA_Handle_TypeDef structure that contains
 *                      the configuration information for DMA
 *  @return             None
 *  @details
 *      This function Should not be modified, when the callback is needed,
 *      the HAL_DMA_MspDeInit could be implemented in the user file
 */
__weak void HAL_DMA_MspDeInit(HAL_DMA_Handle_TypeDef *hDMA)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hDMA);
}

__weak void HAL_DMA_Error_Callback(HAL_DMA_Handle_TypeDef *hDMA)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hDMA);
}

__weak void HAL_DMA_Done_Callback(HAL_DMA_Handle_TypeDef *hDMA, uint32_t channels)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hDMA);
}


static int
_DMA_Verify_Descriptor_Base_Addr(
    HAL_DMA_Handle_TypeDef  *hDMA,
    uint32_t                addr)
{
    uint32_t        addr_mask = 0ul;
    uint32_t        channel_num = 0;

    channel_num = (hDMA->Instance->STA & PDMA_STA_CHNLS_MINUS1_Msk) >> PDMA_STA_CHNLS_MINUS1_Pos;
    channel_num += 1;

    addr_mask = (channel_num > 16) ? 0x3FFul :
                (channel_num > 8) ? 0x1FFul :
                (channel_num > 4) ? 0xFFul :
                (channel_num > 1) ? 0x7Ful : 0x3Ful;

    return (addr & addr_mask) ? -1 : 0;
}

static HAL_StatusTypeDef
_DMA_Wait_Channel_Idle(
    HAL_DMA_Handle_TypeDef      *hDMA,
    HAL_DMA_Channel_TypeDef     channal_id,
    DMA_Descriptor_Type_t       type,
    int                         timeout)
{
    if( __HAL_DMA_IS_CHANNEL_ENABLE(hDMA, channal_id) )
        return HAL_OK;

    if( (type == DMA_DESCRIPTOR_TYPE_ALTERNATE &&
            READ_BIT(hDMA->Instance->CHALTSET, channal_id)) ||
        (type == DMA_DESCRIPTOR_TYPE_PRIMARY &&
            !READ_BIT(hDMA->Instance->CHALTSET, channal_id)) )
    {
        DMA_Descriptor_t    *pDesc = 0;
        DMA_Ctrl_Data_t     *pCtrl = 0;
        uint32_t            start_tick = HAL_GetTick();
        int                 chnnl_index = 0;

        chnnl_index = 31 - __CLZ(channal_id);

        pDesc = (type == DMA_DESCRIPTOR_TYPE_ALTERNATE)
              ? &g_dma_descriptors[0].Alternate[chnnl_index]
              : &g_dma_descriptors[0].Primary[chnnl_index];

        pCtrl = (DMA_Ctrl_Data_t*)&pDesc->ctrl;

        while( pCtrl->CTRL_b.cycle_ctrl != DMA_CYCLE_CTRL_INVALID &&
               pCtrl->CTRL_b.n_minus_1 )
        {
            // check timeout
            if( timeout > 0 && timeout < (int)(HAL_GetTick() - start_tick) )
                return HAL_TIMEOUT;
        }
    }

    return HAL_OK;
}
//=============================================================================
//                  Public Function Definition
//=============================================================================
HAL_StatusTypeDef
HAL_DMA_Init(HAL_DMA_Handle_TypeDef *hDMA)
{
    assert_param(hDMA);
    assert_param(hDMA->Instance);

    /* Check the DMA handle */
    if( hDMA == NULL || hDMA->Instance == NULL )
    {
        return HAL_ERROR;
    }

    if( hDMA->State == HAL_DMA_STATE_RESET )
    {
        __HAL_UNLOCK(hDMA);
        HAL_DMA_MspInit(hDMA);
    }

    __HAL_RCC_DMA_FORCE_RESET();
    __HAL_RCC_DMA_RELEASE_RESET();

    __HAL_RCC_DMA_CLK_ENABLE();

    hDMA->State = HAL_DMA_STATE_BUSY;
    if( ((hDMA->Instance->STA & PDMA_STA_CHNLS_MINUS1_Msk) >> PDMA_STA_CHNLS_MINUS1_Pos)
                != (DMA_CHANNEL_MAX - 1) )
    {
        /* Channel number is NOT match */
        hDMA->State = HAL_DMA_STATE_ERROR;
        return HAL_ERROR;
    }

    //-----------------------
    /* Configure DMA */
    memset(&g_dma_descriptors, 0x0, sizeof(g_dma_descriptors));

    hDMA->Instance->CFG    = 0;                                                      // Disable DMA controller for configuration
    hDMA->Instance->PDBPTR = (uint32_t)&g_dma_descriptors->Primary->src_addr_end;    // Set DMA Primary Ctrl Data address

    __HAL_DMA_DISABLE_CHANNEL(hDMA, 0xFFFFFFFF);    // Disable all channels

    hDMA->Instance->CHENCLR = 0xFFFFFFFFul;

    hDMA->Instance->CFG = PDMA_CFG_ENABLE_Msk;      // Enable DMA controller

    /* HAL DMA global status */
    hDMA->State = HAL_DMA_STATE_READY;

    return HAL_OK;
}


HAL_StatusTypeDef
HAL_DMA_DeInit(HAL_DMA_Handle_TypeDef *hDMA)
{
    assert_param(hDMA);

    /* Check the DMA handle */
    if( hDMA == NULL )
    {
        return HAL_ERROR;
    }

    __HAL_LOCK(hDMA);

    HAL_DMA_MspDeInit(hDMA);

    __HAL_RCC_DMA_FORCE_RESET();
    __HAL_RCC_DMA_RELEASE_RESET();

    __HAL_DMA_DISABLE_IT(hDMA, 0xFFFFFFFF);

    hDMA->Instance->CFG = 0;

    __HAL_DMA_DISABLE_CHANNEL(hDMA, 0xFFFFFFFF);    /* Disable all channels */

    hDMA->Instance->CHENCLR = 0xFFFFFFFFul;

    hDMA->State = HAL_DMA_STATE_RESET;

    __HAL_RCC_DMA_CLK_DISABLE();

    __HAL_UNLOCK(hDMA);

    /* Return function status */
    return HAL_OK;
}

HAL_StatusTypeDef
HAL_DMA_Set_Channel(
    HAL_DMA_Handle_TypeDef      *hDMA,
    HAL_DMA_Channel_TypeDef     channal_id,
    HAL_DMA_Channel_Cfg_TypeDef *pCfg)
{
    int         chnnl_index = 0;

    chnnl_index = 31 - __CLZ(channal_id);

    assert_param(hDMA);
    assert_param(chnnl_index < DMA_CHANNEL_MAX);

    if( chnnl_index >= DMA_CHANNEL_MAX || !hDMA )
    {
        return HAL_ERROR;
    }

    __HAL_LOCK(hDMA);

    if( pCfg->is_high_priority )    hDMA->Instance->CHPRISET |= channal_id;
    else                            hDMA->Instance->CHPRICLR |= channal_id;

    if( pCfg->is_burst_mode )       hDMA->Instance->CHBTSET |= channal_id;
    else                            hDMA->Instance->CHBTCLR |= channal_id;

    if( pCfg->is_peri_trigger )     hDMA->Instance->CHRMKCLR |= channal_id;
    else                            hDMA->Instance->CHRMKSET |= channal_id;

    if( pCfg->is_channel_switch )   hDMA->Instance->CHSELREQ |= channal_id;
    else                            hDMA->Instance->CHSELREQ &= ~channal_id;

    /* interrupt */
    if( pCfg->is_enable_interrupt )  __HAL_DMA_ENABLE_IT(hDMA, channal_id);
    else                             __HAL_DMA_DISABLE_IT(hDMA, channal_id);

    __HAL_UNLOCK(hDMA);

    return HAL_OK;
}

HAL_StatusTypeDef
HAL_DMA_Set_Descriptor(
    HAL_DMA_Handle_TypeDef          *hDMA,
    HAL_DMA_Channel_TypeDef         channal_id,
    HAL_DMA_Descriptor_Cfg_TypeDef  *pCfg,
    DMA_Descriptor_t                *pDesc)
{
    int                 chnnl_index = 0;
    DMA_Descriptor_t    *pDst_desc = 0;
    DMA_Ctrl_Data_t     *pCtrl = 0;
    HAL_StatusTypeDef   status = HAL_OK;

    chnnl_index = 31 - __CLZ(channal_id);

    assert_param(hDMA);
    assert_param(chnnl_index < DMA_CHANNEL_MAX);

    /* check parameters */
    if( chnnl_index >= DMA_CHANNEL_MAX || !hDMA )
    {
        return HAL_ERROR;
    }

    if( pCfg->xfer_bytes & ((0x1 << pCfg->unit_size) - 1) )
        return HAL_ERROR;

    /* check address alignment */
    if( (((uint32_t)pCfg->src_addr >> pCfg->unit_size) << pCfg->unit_size) != (uint32_t)pCfg->src_addr ||
        (((uint32_t)pCfg->dst_addr >> pCfg->unit_size) << pCfg->unit_size) != (uint32_t)pCfg->dst_addr )
        return HAL_ERROR;

    __HAL_LOCK(hDMA);

    do {
        status = _DMA_Wait_Channel_Idle(hDMA, channal_id, pCfg->type, pCfg->timeout);
        if( status != HAL_OK )      break;

        /* select the destination descriptor */
        pDst_desc = (pCfg->type == DMA_DESCRIPTOR_TYPE_USER)      ? pDesc
                  : (pCfg->type == DMA_DESCRIPTOR_TYPE_ALTERNATE) ? &g_dma_descriptors[0].Alternate[chnnl_index]
                  : &g_dma_descriptors[0].Primary[chnnl_index];

        if( !pDst_desc )
        {
            status = HAL_ERROR;
            break;
        }

        pCtrl = (DMA_Ctrl_Data_t*)&pDst_desc->ctrl;
        pCtrl->CTRL = 0ul;

        pCtrl->CTRL_b.cycle_ctrl = (pCfg->mode == HAL_DMA_MODE_AUTO)      ? DMA_CYCLE_CTRL_AUTO
                                 : (pCfg->mode == HAL_DMA_MODE_PING_PONG) ? DMA_CYCLE_CTRL_PINGPONG
                                 : (pCfg->mode == HAL_DMA_MODE_SG)        ? DMA_CYCLE_CTRL_MEM_SCATTER_GATHER_ALT
                                 : (pCfg->mode == HAL_DMA_MODE_PERI_SG)   ? DMA_CYCLE_CTRL_PERI_SCATTER_GATHER_ALT
                                 : (pCfg->mode == HAL_DMA_MODE_BASIC)     ? DMA_CYCLE_CTRL_BASIC
                                 : DMA_CYCLE_CTRL_INVALID;

        if( pCfg->type == DMA_DESCRIPTOR_TYPE_PRIMARY &&
            (pCfg->mode == HAL_DMA_MODE_PING_PONG ||
             pCfg->mode == HAL_DMA_MODE_SG ||
             pCfg->mode == HAL_DMA_MODE_PERI_SG) )
            pCtrl->CTRL_b.cycle_ctrl = DMA_CYCLE_CTRL_INVALID;

        pCtrl->CTRL_b.n_minus_1  = (pCfg->xfer_bytes >> pCfg->unit_size) - 1;

        pCtrl->CTRL_b.src_size   = pCfg->unit_size;
        pCtrl->CTRL_b.dst_size   = pCfg->unit_size;

        pCtrl->CTRL_b.src_inc    = pCfg->src_inc;
        pCtrl->CTRL_b.dst_inc    = pCfg->dst_inc;

        pCtrl->CTRL_b.r_power    = pCfg->arbit_rate;

        pCtrl->CTRL_b.src_prot_ctrl = 0;

        pDst_desc->user         = pCfg->user_data;
        pDst_desc->src_addr_end = (pCfg->src_inc == DMA_DATA_INC_NONE)
                                ? (void*)pCfg->src_addr
                                : (void*)((uint32_t)pCfg->src_addr + (pCtrl->CTRL_b.n_minus_1 << pCfg->src_inc));

        if( pCfg->type == DMA_DESCRIPTOR_TYPE_PRIMARY &&
            (pCfg->mode == HAL_DMA_MODE_SG || pCfg->mode == HAL_DMA_MODE_PERI_SG) )
        {
            pDst_desc->dst_addr_end = (void*)((uint32_t)pCfg->dst_addr + sizeof(DMA_Descriptor_t) - 4);
        }
        else
        {
            pDst_desc->dst_addr_end = (pCfg->dst_inc == DMA_DATA_INC_NONE)
                                    ? (void*)pCfg->dst_addr
                                    : (void*)((uint32_t)pCfg->dst_addr + (pCtrl->CTRL_b.n_minus_1 << pCfg->dst_inc));
        }
    } while(0);

    __HAL_UNLOCK(hDMA);

    return status;
}

HAL_StatusTypeDef
HAL_DMA_Get_Descriptor(
    HAL_DMA_Handle_TypeDef          *hDMA,
    HAL_DMA_Channel_TypeDef         channal_id,
    DMA_Descriptor_Type_t           type,
    HAL_DMA_Descriptor_Cfg_TypeDef  *pCfg)
{
    int                 chnnl_index = 0;
    DMA_Descriptor_t    *pAct_desc = 0;
    DMA_Ctrl_Data_t     *pCtrl = 0;

    chnnl_index = 31 - __CLZ(channal_id);

    assert_param(hDMA);
    assert_param(pCfg);
    assert_param(chnnl_index < DMA_CHANNEL_MAX);

    /* check parameters */
    if( chnnl_index >= DMA_CHANNEL_MAX || !hDMA || !pCfg )
    {
        return HAL_ERROR;
    }

    pAct_desc = (type == DMA_DESCRIPTOR_TYPE_ALTERNATE)
              ? &g_dma_descriptors[0].Alternate[chnnl_index]
              : &g_dma_descriptors[0].Primary[chnnl_index];

    pCtrl = (DMA_Ctrl_Data_t*)&pAct_desc->ctrl;

    /* check a descriptor to be used or not */
    if( !pCtrl->CTRL_b.n_minus_1 )
        return HAL_ERROR; // this descriptor already be used

    pCfg->user_data  = pAct_desc->user;
    pCfg->unit_size  = (DMA_Data_Size_t)pCtrl->CTRL_b.dst_size;
    pCfg->src_inc    = (DMA_Data_Inc_t)pCtrl->CTRL_b.src_inc;
    pCfg->dst_inc    = (DMA_Data_Inc_t)pCtrl->CTRL_b.dst_inc;
    pCfg->arbit_rate = (DMA_Arbitration_Rate_t)pCtrl->CTRL_b.r_power;

    pCfg->xfer_bytes = (pCtrl->CTRL_b.n_minus_1 + 1) << pCtrl->CTRL_b.dst_size;

    pCfg->src_addr = (pCtrl->CTRL_b.src_inc == DMA_DATA_INC_NONE)
                   ? (void*)pAct_desc->src_addr_end
                   : (void*)((uint32_t)pAct_desc->src_addr_end - (pCtrl->CTRL_b.n_minus_1 << pCtrl->CTRL_b.src_inc));

    if( pCfg->type == DMA_DESCRIPTOR_TYPE_PRIMARY &&
        (pCfg->mode == HAL_DMA_MODE_SG || pCfg->mode == HAL_DMA_MODE_PERI_SG) )
    {
        pCfg->dst_addr = (void*)((uint32_t)pAct_desc->dst_addr_end - sizeof(DMA_Descriptor_t) + 4);
    }
    else
    {
        pCfg->dst_addr = (pCtrl->CTRL_b.dst_inc == DMA_DATA_INC_NONE)
                       ? (void*)pAct_desc->dst_addr_end
                       : (void*)((uint32_t)pAct_desc->dst_addr_end - (pCtrl->CTRL_b.n_minus_1 << pCtrl->CTRL_b.dst_inc));
    }

    return HAL_OK;
}

HAL_StatusTypeDef
HAL_DMA_Channel_Sart(
    HAL_DMA_Handle_TypeDef      *hDMA,
    HAL_DMA_Channel_TypeDef     channal_id,
    HAL_DMA_Xfer_Cfg_TypeDef    *pCfg)
{
    int                 chnnl_index = 0;
    HAL_StatusTypeDef   status = HAL_OK;

    chnnl_index = 31 - __CLZ(channal_id);

    assert_param(hDMA);
    assert_param(pCfg);
    assert_param(chnnl_index < DMA_CHANNEL_MAX);

    if( !pCfg || !hDMA )
        return HAL_ERROR;

    if( chnnl_index >= DMA_CHANNEL_MAX )
        return HAL_ERROR;

    #if 0
    {   // wait DMA idle (safer)
        DMA_State_t     state = DMA_STATE_IDLE;
        uint32_t        start_tick = HAL_GetTick();

        do {
            state = (DMA_State_t)__HAL_DMA_GET_STATE(hDMA);

            // check timeout
            if( timeout > 0 && timeout < (int)(HAL_GetTick() - start_tick) )
                return HAL_TIMEOUT;
        } while( state != DMA_STATE_IDLE && state != DMA_STATE_DONE && state != DMA_STATE_STALLED );
    }
    #endif

    __HAL_LOCK(hDMA);

    do {   // configure DMA Ctrl Data Structure
        DMA_Descriptor_t    *pDesc = 0;
        DMA_Ctrl_Data_t     *pCtrl = 0;

        status = _DMA_Wait_Channel_Idle(hDMA, channal_id, pCfg->type, pCfg->timeout);
        if( status != HAL_OK )  break;

        /* use Primary/Alternate pool */
        if( pCfg->type == DMA_DESCRIPTOR_TYPE_PRIMARY ||
            pCfg->mode == HAL_DMA_MODE_PING_PONG ||
            pCfg->mode == HAL_DMA_MODE_SG ||
            pCfg->mode == HAL_DMA_MODE_PERI_SG)
        {
            SET_BIT(hDMA->Instance->CHALTCLR, channal_id);
            pDesc = &g_dma_descriptors[0].Primary[chnnl_index];
        }
        else if( pCfg->type == DMA_DESCRIPTOR_TYPE_ALTERNATE )
        {
            SET_BIT(hDMA->Instance->CHALTSET, channal_id);
            pDesc = &g_dma_descriptors[0].Alternate[chnnl_index];
        }
        else
        {
            status = HAL_ERROR;
            break;
        }

        pCtrl = (DMA_Ctrl_Data_t*)&pDesc->ctrl;

        pCtrl->CTRL_b.cycle_ctrl = (pCfg->mode == HAL_DMA_MODE_AUTO)      ? DMA_CYCLE_CTRL_AUTO
                                 : (pCfg->mode == HAL_DMA_MODE_PING_PONG) ? DMA_CYCLE_CTRL_PINGPONG
                                 : (pCfg->mode == HAL_DMA_MODE_SG)        ? DMA_CYCLE_CTRL_MEM_SCATTER_GATHER
                                 : (pCfg->mode == HAL_DMA_MODE_PERI_SG)   ? DMA_CYCLE_CTRL_PERI_SCATTER_GATHER
                                 : DMA_CYCLE_CTRL_BASIC;

        __HAL_DMA_WATCH_CHANNEL(hDMA, channal_id);

        __HAL_DMA_ENABLE_CHANNEL(hDMA, channal_id);

        if( pCfg->is_immediate )
            __HAL_DMA_FORCE_START(hDMA, channal_id);

    } while(0);

    __HAL_UNLOCK(hDMA);

    return status;
}


HAL_StatusTypeDef
HAL_DMA_Channel_Stop(
    HAL_DMA_Handle_TypeDef  *hDMA,
    HAL_DMA_Channel_TypeDef channal_id)
{
    int                 chnnl_index = 0;
    DMA_Descriptor_t    *pDesc = 0;
    DMA_Ctrl_Data_t     *pCtrl = 0;

    chnnl_index = 31 - __CLZ(channal_id);

    assert_param(hDMA);
    assert_param(chnnl_index < DMA_CHANNEL_MAX);

    if( !hDMA )
        return HAL_ERROR;

    if( chnnl_index >= DMA_CHANNEL_MAX )
        return HAL_ERROR;

    __HAL_LOCK(hDMA);

    __HAL_DMA_DISABLE_IT(hDMA, channal_id);

    /* Force to STOP channel */
    __HAL_DMA_DISABLE_CHANNEL(hDMA, channal_id);

    pDesc = (READ_BIT(hDMA->Instance->CHALTSET, channal_id))
          ? &g_dma_descriptors[0].Alternate[chnnl_index]
          : &g_dma_descriptors[0].Primary[chnnl_index];

    pCtrl = (DMA_Ctrl_Data_t*)&pDesc->ctrl;

    pCtrl->CTRL_b.cycle_ctrl = DMA_CYCLE_CTRL_INVALID;

    while( pCtrl->CTRL_b.n_minus_1 )
    {
        int     timeout = 1000;

        while(timeout--) {}

        if( pCtrl->CTRL_b.cycle_ctrl == DMA_CYCLE_CTRL_INVALID )
            break;
    }

    *(volatile uint32_t*)&pDesc->ctrl = 0ul;

    pDesc = (!READ_BIT(hDMA->Instance->CHALTSET, channal_id))
          ? &g_dma_descriptors[0].Alternate[chnnl_index]
          : &g_dma_descriptors[0].Primary[chnnl_index];

    *(volatile uint32_t*)&pDesc->ctrl = 0ul;

    __HAL_UNLOCK(hDMA);

    return HAL_OK;
}

HAL_StatusTypeDef
HAL_DMA_Verify_Descriptor_Base_Addr(
    HAL_DMA_Handle_TypeDef  *hDMA,
    uint32_t                addr)
{
    return (_DMA_Verify_Descriptor_Base_Addr(hDMA, addr)) ? HAL_ERROR : HAL_OK;
}


void HAL_DMA_IRQHandler(HAL_DMA_Handle_TypeDef *hDMA)
{
    if( __HAL_DMA_IS_ERROR_STATUS(hDMA) )
    {
        __HAL_DMA_CLR_ERROR_STATUS(hDMA);
        HAL_DMA_Error_Callback(hDMA);
        return;
    }

    if( __HAL_DMA_IS_CHANNEL_IRQ(hDMA, 0xFFFFFFFFul) )
    {
        uint32_t   irq_set = 0ul;

        irq_set = hDMA->Instance->CHTCIF;

        __HAL_DMA_CLR_CHANNEL_IRQ_FLAG(hDMA, irq_set);

        HAL_DMA_Done_Callback(hDMA, irq_set);
    }

    return;
}

#endif

/**
  * @}
  */ /* group DMA DMA */

/**
  * @}
  */ /* group ZB32L03x_HAL_Driver */
